/*
 * ServerCorePre.java
 *
 * Created on December 20, 2006, 7:51 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package net.sf.iSnake.server;

import java.awt.Color;
import java.util.Vector;
import net.sf.iSnake.core.*;

/** This Class Processes the data Received from each Client.
 *
 * @author Suraj
 *
 */
public class ServerCorePre extends Thread {
    public static int MAX_CLIENT=10;
    private boolean preMode;
    private boolean postMode;
    private ServerNetworkInterface ServerInterface=null;
    private ServerCorePost ServerPost=null;
    private Color[] ClientColor;
    private Vector player;
    private int thisClient;                                 //the Current Client Whose data is being Processed
    private premodeISCPpacket packet=null;
    private boolean handle=false;                           //whether the server is busy(true) or not(false)
    private boolean Stop=false;
    private ServerStatus ss;
    private ServerIntermediate si=null;
    
    private WallGenerator wallGen;
    
    /**Forwards the status message to the status box
     *@param status The status code
     *@param message The message to display
     */
    public void status(short status,String message){
        ss.status(status,message);
    }
    /**Hids or shows the Server Status box (Toggles)
     */
    public void statusHideShow(){
        if (ss.isVisible()){
            ss.setVisible(false);
        } else
            ss.setVisible(true);
    }
    /**Creates a new instance of ServerCorePre
     *@param port The Port no at which the server should listen/start
     */
    public ServerCorePre(int port) {
        ClientColor=new Color[MAX_CLIENT];
        player=new Vector();
        ss=new ServerStatus();
        this.setPreMode(true);
        this.setPostMode(false);
        ServerPost=new ServerCorePost(this);
        this.ServerInterface=new ServerNetworkInterface(this,ServerPost,port);
        ServerPost.setServerInterface(ServerInterface);
        ss.setVisible(true);
        this.start();
    }
    /**Safely Closes the server
     *@deprecated The Server Should not be Stopped
     */
    public void close(){
        Stop=true;
    }
    /**Generates random wall returns the vector of the wall
     */
    private Short[][] getWallCoordinates(){
        Short[][]  wall=new Short[2][];
        wall[0]=new Short[2];
        wall[1]=new Short[2];
        wall[0][0]=new Short("10");
        wall[0][1]=new Short("10");
        wall[1][0]=new Short("10");
        wall[1][1]=new Short("11");
        return wall;
    }
    
    /** Mediator state between the pre and post mode
     */
    private void syncAndSwitch(){
        status(ServerStatus.DEBUG_INFO,"Game is starting...");
        //STEP 1: sending First Synchronization signal
        //signal: PRE_CMD_PrepareForGameStart
        premodeISCPpacket syncPacket=new premodeISCPpacket();
        syncPacket.setPremodeCommand(ISCP.PRE_CMD_PrepareForGameStart);
        ServerInterface.send(syncPacket);
        wallGen=new WallGenerator(this);
        status(ServerStatus.DEBUG_INFO,"1st Sync signal send...");
        
        //send Wall Coordinates
        status(ServerStatus.DEBUG_INFO,"Sending Wall Coordinates...");
        syncPacket=new premodeISCPpacket();
        syncPacket.setPremodeCommand(ISCP.PRE_CMD_WallCoordinate);
        syncPacket.setWallCoordinates(wallGen.getWallCoordinate());
        ServerInterface.send(syncPacket);
        status(ServerStatus.DEBUG_INFO,"Wall Coordinates Send...");
        
        //send Snake Start Positions
        status(ServerStatus.DEBUG_INFO,"Sending Snake head Coordinates...");
        syncPacket=new premodeISCPpacket();
        syncPacket.setPremodeCommand(ISCP.PRE_CMD_SnakeStartCoordinate);
        Vector v=(Vector)wallGen.getSnakeCoordinates().clone();
        syncPacket.setSnakeStartPosition(v);
        ServerInterface.send(syncPacket);
        this.ServerPost.setStartXY((Vector) v.clone());
        status(ServerStatus.DEBUG_INFO,"Snake head send...");
        
        //send Game speed
        status(ServerStatus.DEBUG_INFO,"Sending Game Speed...");
        syncPacket=new premodeISCPpacket();
        syncPacket.setPremodeCommand(ISCP.PRE_CMD_GameSpeed);
        syncPacket.setGameSpeed(ServerPost.getGameSpeed());
        ServerInterface.send(syncPacket);
        status(ServerStatus.DEBUG_INFO,"Game speed send...");
        //TODO send it to post mode server too
        
        //send the Food Coordinate==>it will be done later by post-mode server
        //Server will switch to post Mode
        this.setPreMode(false);                 // this will automaticalley stop the preModeServer
        ServerInterface.close();            // Stops the Client acceptor thread
        throwPendingClients();              // Throw the pending Clients
        //ServerPost.start();               // starts the postModeServer
        si=new ServerIntermediate(this,this.getTotalClients());
        si.start();
        
        //STEP :2 Send "Switch to post Mode" signal
        //signal: PRE_CMD_Switch_To_PostMode
        status(ServerStatus.DEBUG_INFO,"Switch To POST MODEsignal sending...");
        syncPacket=new premodeISCPpacket();
        syncPacket.setPremodeCommand(ISCP.PRE_CMD_Switch_To_PostMode);
        ServerInterface.send(syncPacket);
        status(ServerStatus.DEBUG_INFO,"Switch To POST MODEsignal send...");
        
        this.handle=false;                  // To ensure No any data will be processed further by server
        status(ServerStatus.DEBUG_INFO,"Waiting for Sync. signal from all players...");
    }
    /**Starts the game if no any Client are in pending state
     *@return true if the game is started.false if some Clients are in
     *pending state to join
     */
    public boolean startGame(){
        if(getByColor(Color.BLACK)!=-1){
            //At least one Client is in pending state
            return false;
        } else {
            syncAndSwitch();
            return true;
        }
    }
    /**Starts the game Forcefully. And rejects the pending Client
     */
    public void forceStartGame(){
        syncAndSwitch();
    }
    public synchronized void run(){
        short cmd;
        int i;
        PlayerPreferences playerPref;
        //TODO during switching to pre-session start postsession thread
        while(!Stop){
            if(handle && isPreMode()){
                //process the data here
                cmd=packet.getPremodeCommand();
                if(cmd==ISCP.PRE_CMD_ChatMessage){
                    status(ServerStatus.DEBUG_MESSAGE,packet.getChatMessageSenderId()+" TO "+
                            packet.getChatMessageReceiverId()+"  "+packet.getChatMessage());
                    //sends the message to the corresponding Client
                    ServerInterface.send(packet.getChatMessageReceiverId(),packet);
                } else if(cmd==ISCP.PRE_CMD_LocalPlayerPreference){
                    playerPref = packet.getLocalPlayerPereference();
                    status(ServerStatus.DEBUG_INFO,"Player Preferences received for player ID: "+thisClient+"  "+playerPref);
                    //if the color is available
                    if(getByColor(playerPref.getSnakeColor())==-1){
                        player.add(playerPref.clone());
                        ClientColor[playerPref.getPlayer_id()]=playerPref.getSnakeColor();
                        //the Client is verified
                        premodeISCPpacket tempPreModelISCP=new premodeISCPpacket();
                        tempPreModelISCP.setPremodeCommand(ISCP.PRE_CMD_OKJoinGame);
                        ServerInterface.send(playerPref.getPlayer_id(),tempPreModelISCP);
                        status(ServerStatus.DEBUG_INFO,"Player Preferences VERIFIED for player ID: "+thisClient);
                        
                        //send the player Preferences(vector) to all (including this) client
                        tempPreModelISCP=new premodeISCPpacket();
                        tempPreModelISCP.setPremodeCommand(ISCP.PRE_CMD_RemotePlayerPreference);
                        tempPreModelISCP.setRemotePlayerPreference(player);
                        ServerInterface.send(tempPreModelISCP);
                        status(ServerStatus.DEBUG_INFO,"Remote player Preferences Updated and send to all");
                    }
                    //if the color is already choosen
                    else {
                        status(ServerStatus.DEBUG_ERROR,"Player Preferences ERROR! for player ID: "+thisClient+" Requested Color Unavailable...");
                        premodeISCPpacket tempPreModelISCP=new premodeISCPpacket();
                        tempPreModelISCP.setPremodeCommand(ISCP.PRE_CMD_ERRJoinGame);
                        ServerInterface.send(playerPref.getPlayer_id(),tempPreModelISCP);
                    }
                } else{
                    //Unknown Command
                    status(ServerStatus.DEBUG_INFO,"Unknown COMMAND "+cmd+" FORM PlayerId: "+thisClient);
                }
                handle=false;
                notifyAll();
            } else{
                try {
                    this.wait();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
    /**
     * Sends the string to ServerCorePre thread to Manipulate
     * @param c The client that send the Current object
     * @param obj The object to be Manipulated
     */
    public synchronized void forwardPacket(int c,Object obj){
        while(true){
            if(!handle){
                this.thisClient=c;
                try{
                    this.packet=(premodeISCPpacket)obj;
                }catch(Exception ex){}
                handle=true;
                break;
            } else {
                try {
                    wait();
                } catch (InterruptedException ex) {}
            }
        }
        System.out.println("Object send to Process...");
        notifyAll();
    }
    /** Removes the Client associated with the ClientNo.It removes the traces of the Client after the
     *client leaves the game.
     *  @param no The Client no
     */
    public void removeClient(int no){
        for(int i=0;i<player.size();i++){
            if(((PlayerPreferences)player.get(i)).getPlayer_id()==no){
                player.remove(i);
                break;
            }
        }
        ClientColor[no]=null;
    }
    /** Calculate returns the Lowest available Client No
     *@return Returns the Lowest available Client No. Returns -1 if
     *the Server is full.
     */
    public int getAvailable(){
        for(int i=1;i<MAX_CLIENT;i++){
            if(ClientColor[i]==null){
                // Black Color is unavailable for any client so can be used for reservation
                ClientColor[i]=Color.black;
                return (i);
            }
        }
        return (-1);
    }
    /**Returns the Client no by Client Color
     *@param c The color of the client
     *@return the Client no which has the specified Color.
     *returns -1 if the color is not choosen yet
     */
    private int getByColor(Color c){
        for(int i=1;i<MAX_CLIENT;i++){
            try{
                if(ClientColor[i].equals(c)) return (i);
            } catch(Exception e){}
        }
        return (-1);
    }
    /**Throws all the Pending Clients
     */
    private void throwPendingClients() {
        //throw the Pending Clients
        premodeISCPpacket throwClientPacket=new premodeISCPpacket();
        throwClientPacket.setPremodeCommand(ISCP.PRE_CMD_PlayerThrownOut);
        int no;
        while((no=getByColor(Color.BLACK))!=-1){
            ClientColor[no]=null;
            ServerInterface.send((short)no,throwClientPacket);
            ServerInterface.closeClient(no);
        }
    }
    public static void  main(String arg[]){
        new ServerCorePre(9669);
    }
    
    public boolean isPreMode() {
        return preMode;
    }
    
    public void setPreMode(boolean preMode) {
        this.preMode = preMode;
    }
    
    public boolean isPostMode() {
        return postMode;
    }
    
    public void setPostMode(boolean postMode) {
        this.postMode = postMode;
    }
    
    public static int getMAX_CLIENT() {
        return MAX_CLIENT;
    }
    
    public int getTotalClients() {
        int total=0;
        for(int i=1;i<MAX_CLIENT;i++){
            if(ClientColor[i]!=null && ClientColor[i]!=Color.BLACK)
                total++;
        }
        return total;
    }
    
    public ServerCorePost getServerPost() {
        return ServerPost;
    }
    
    public ServerIntermediate getSi() {
        return si;
    }
    
    public boolean isPlayer(int no){
        if(ClientColor[no]!=null && ClientColor[no]!=Color.BLACK){
            return true;
        } else return false;
    }
    
    public WallGenerator getWallGen() {
        return wallGen;
    }
}
